package cn.fscode.sign.client;

import cn.fscode.sign.client.crypto.BaseAsymmetricCrypto;
import cn.fscode.sign.client.model.UploadSecretKeyReq;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author shenguangyang
 */
@Slf4j
public class SignManagerImpl implements SignManager {
    @Resource(name = "signatureAsymmetricCrypto")
    private BaseAsymmetricCrypto asymmetricCrypto;

    @Resource
    private SignatureProperties signatureProperties;

    private String secretKey = "";

    @PostConstruct
    public void init() {
        secretKey = RandomUtil.randomString(32);
        String encryptStr = asymmetricCrypto.encrypt(secretKey, KeyType.PublicKey, null, signatureProperties.getClientSecret());
        UploadSecretKeyReq req = new UploadSecretKeyReq();
        req.setClientId(signatureProperties.getClientId());
        req.setCryptoType(signatureProperties.getSignType().name());
        req.setEncryptedStr(encryptStr);
        String body = HttpRequest.post(signatureProperties.getBaseUrl() + signatureProperties.getUploadSecretKeyUri())
                .body(JSON.toJSONString(req)).execute().body();
        if (!"1".equals(body)) {
            throw new RuntimeException("sign init secret key fail, resp body: " + body);
        }
    }


    @Override
    public Map<String, String> getSignHeader(Object requestData) {
        Map<String, Object> data = new HashMap<>();
        CopyOptions copyOptions = CopyOptions.create();
        copyOptions.ignoreNullValue();

        BeanUtil.beanToMap(requestData, data, copyOptions);


        Map<String, String> headerMap = new HashMap<>();
        headerMap.put(RequestHeader.CLIENT_ID, signatureProperties.getClientId());
        headerMap.put(RequestHeader.NONCE, UUID.randomUUID(true).toString());
        headerMap.put(RequestHeader.TIME_STAMP, String.valueOf(System.currentTimeMillis()));
        headerMap.put(RequestHeader.SIGN_TYPE, signatureProperties.getSignType().name());

        data.putAll(headerMap);
        String sortParamStr = sortParams(data, SortRule.ASE);
        AES aes = new AES(Mode.ECB, Padding.ZeroPadding,
                new SecretKeySpec(secretKey.getBytes(), "AES"));
        String encryptStr = StrUtil.str(aes.encryptHex(sortParamStr), StandardCharsets.UTF_8);
        headerMap.put(RequestHeader.SIGN, encryptStr);
        return headerMap;
    }

    public static String sortParams(Map<String, Object> params, SortRule sortRule) {
        StringBuilder sb = new StringBuilder();
        //step1：先对请求参数排序
        TreeSet<String> sortSet = new TreeSet<>((o1, o2) -> sortRule == SortRule.ASE ? o1.compareTo(o2) : o1.compareTo(o2) * -1);
        sortSet.addAll(params.keySet());
        // step2：把参数的key value链接起来 secretKey放在最后面，得到要加密的字符串
        for (String key : sortSet) {
            Object obj = params.get(key);
            if (Objects.isNull(obj)) {
                continue;
            }
            Object value;
            if (obj instanceof String) {
                value = obj;
            } else if (obj instanceof Collection && ((Collection<?>) obj).size() == 1) {
                value = ((Collection<?>) obj).stream().findFirst().get();
            } else {
                value = JSON.toJSONString(obj);
            }
            sb.append(key).append(value);
        }
        return sb.toString();
    }
}
